﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SmartChess
{
    class MiniMaxBrain : Brain
    {
        protected Random _rnd;
        public MiniMaxBrain()
        {
            _rnd = new Random();
        }
        public int depth;
        public int algorithmus;
        TimeSpan timeValue;
        int countValue = 0;
        public override async Task<ChessMove> SelectMoveAsync(Game game, int player)
        {
            return await Task.Run(() =>
            {
                Stopwatch timer = Stopwatch.StartNew();
                countValue = 0;
                int bestValue = -int.MaxValue;
                var bestMoves = new List<ChessMove>();
                Parallel.ForEach(game.MovesFor(player), move =>
              //foreach (var move in game.MovesFor(player))
               {
                   int value;
                   if (algorithmus == 0)
                       value = -MiniMax(game.CopyAndMove(move), 1 - player, depth);
                   else
                       value = -AlphaBeta(game.CopyAndMove(move), 1 - player, depth, -int.MaxValue, -bestValue);
                   lock (bestMoves) //Verhindert, dass bestMoves von zwei Threads "gleichzeitig" beschrieben wird
                   {
                       if (value > bestValue)
                       {
                           // Neuer bester Zug
                           bestValue = value;
                           bestMoves.Clear();
                           bestMoves.Add(move);
                       }
                       else if (value == bestValue)
                       {
                           // Ein Zug mit derselben Bewertung wie der bisher beste
                           bestMoves.Add(move);
                       }
                   }
               });
                timer.Stop();
                timeValue = timer.Elapsed;
               // Aus den gesammelten Zügen mit der besten Bewertung einen zufällig auswählen.
                return bestMoves[_rnd.Next(bestMoves.Count)];
            });
        }
        public override String GetTimer()
        {
            if (timeValue != null)
                return string.Format("Ich habe {0:n0} Positionen in {1:%h}:{1:mm}:{1:ss},{1:ff} Stunden berechnet.", countValue, timeValue);
            else
                return "Keine Werte";
        }

        protected int MiniMax(Game game, int player, int depth)
        {
            if (depth == 0)
            {
                countValue++;
                return game.GetValue(game, player);
            }
            int bestValue = -int.MaxValue;
            foreach (var move in game.MovesFor(player))
            {
                int value = -MiniMax(game.CopyAndMove(move), 1 - player, depth - 1);
                if (value > bestValue)
                {
                    bestValue = value;
                }
            }
            return bestValue;
        }
        protected virtual int AlphaBeta(Game game, int player, int depth, int alpha, int beta)
        {
            if (depth == 0)
            {
                countValue++;
                return game.GetValue(game, player);
            }
            int bestValue = -int.MaxValue;
            foreach (var move in game.MovesFor(player))
            {
                int value = -AlphaBeta(game.CopyAndMove(move), 1 - player, depth - 1, -beta, -alpha);
                if (value > bestValue)
                {
                    bestValue = value;
                }
                if (value > alpha)
                {
                    alpha = value;
                    if (alpha > beta)
                    {
                        break;
                    }
                }
            }
            return bestValue;
        }
        /*protected Random _rnd;

        public MiniMaxBrain()
        {
            _rnd = new Random();
        }

        int _moveCount = 0;

        public async override Task<ChessMove> SelectMoveAsync(Game game, int player)
        {
            return await Task.Run(() =>
            {
                var moves = (
                  from m in game.MovesFor(player)
                  select m
                  ).ToList();
                _moveCount = moves.Count;
                int idx = _rnd.Next(_moveCount);
                return moves[idx];
            });
        }*/
    }
}
